﻿// <copyright file="MapInfoStringTokeniser.cs" company="QUT">
//     Copyright (c) Ninety Nine Percent. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Qut.GenerateKml.Data
{
    /// <summary>
    /// MapInfo String Tokeniser class.
    /// </summary>
    public class MapInfoStringTokeniser
    {
        /// <summary>
        /// Current line buffer
        /// </summary>
        private string line = string.Empty; 

        /// <summary>
        /// Current extracted token
        /// </summary>
        private string lastToken = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="MapInfoStringTokeniser"/> class.
        /// </summary>
        public MapInfoStringTokeniser()
            : base()
        {
        }

        /// <summary>
        /// Left trims the specified string.
        /// </summary>
        /// <param name="untrimmed">The string.</param>
        /// <returns>The string (left trimmed).</returns>
        public static string LTrim(string untrimmed)
        {
            while ((untrimmed.Length > 0) && (untrimmed[0] == ' '))
            {
                untrimmed = untrimmed.Substring(1);
            }

            return untrimmed;
        }

        /// <summary>
        /// Quotes the string.
        /// </summary>
        /// <param name="unquoted">The string.</param>
        /// <returns>Returns the quoted string.</returns>
        public static string StringQuote(string unquoted)
        {
            return "\"" + unquoted.Replace("\"", "\"\"") + "\"";
        }

        /// <summary>
        /// Removes quotes from the string.
        /// </summary>
        /// <param name="quoted">The quoted.</param>
        /// <returns>Returns the unquoted string.</returns>
        public static string StringUnquote(string quoted)
        {
            if (quoted.StartsWith("\"") && quoted.EndsWith("\"") && (quoted.Length > 1))
            {
                quoted = quoted.Substring(1, quoted.Length - 2).Replace("\"\"", "\"");
            }

            return quoted;
        }

        /// <summary>
        /// Reads the line.
        /// </summary>
        /// <param name="line">The input line.</param>
        /// <returns>Returns the result.</returns>
        public bool ReadLine(string line)
        {
            if (this.line == null)
            {
                this.line = string.Empty;
            }
            else
            {
                this.line = LTrim(line);
            }

            return !line.Equals(string.Empty);
        }

        /// <summary>
        /// Reads the line.
        /// </summary>
        /// <returns>Returns the result.</returns>
        public virtual bool ReadLine()
        {
            return this.ReadLine(string.Empty);
        }

        /// <summary>
        /// Gets the token.
        /// </summary>
        /// <param name="separator">The separator.</param>
        /// <param name="nextLineIfEmpty">if set to <c>true</c> [next line if empty].</param>
        /// <param name="quotedStrings">if set to <c>true</c> [quoted strings].</param>
        /// <returns>Returns the token.</returns>
        public string GetToken(char separator, bool nextLineIfEmpty, bool quotedStrings)
        {
            string token = string.Empty;

            if (this.lastToken != null)
            {
                token = this.lastToken;
                this.lastToken = null;
                return token;
            }

            this.line = LTrim(this.line);

            if (this.line.Equals(string.Empty) && nextLineIfEmpty)
            {
                this.ReadLine();
            }

            if (this.line.Equals(string.Empty))
            {
                return string.Empty;
            }

            int index = -1;

            if (quotedStrings && this.line.StartsWith("\""))
            {
                try
                {
                    index = 1;

                    bool loop = true;
                    int len = this.line.Length;
                    while (loop)
                    {
                        while ((index < len) && (this.line[index] != '"'))
                        {
                            index++;
                        }

                        if ((index < (len - 2)) &&
                                (this.line[index] == '"') &&
                                (this.line[index + 1] == separator))
                        {
                            loop = false;
                        }
                        else if ((index == (len - 1)) &&
                                    (this.line[index] == '"'))
                        {
                            index--;
                            loop = false;
                        }
                        else
                        {
                            index++;
                        }
                    }

                    token = this.line.Substring(1, index).Replace("\"\"", "\"");
                    this.line = LTrim(this.line.Substring(index + 1));

                    if (this.line.Length > 0)
                    {
                        if (this.line[0] == separator)
                        {
                            this.line = this.line.Substring(1);
                        }
                        else
                        {
                            if (separator != ' ')
                            {
                                throw new Exception("Bad separator");
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    throw new Exception("Error reading quoted string");
                }
            }
            else
            {
                index = this.line.IndexOf(separator);

                if (index == -1)
                {
                    token = this.line;
                    this.line = string.Empty;
                }
                else
                {
                    token = LTrim(this.line.Substring(0, index));
                    this.line = LTrim(this.line.Substring(index + 1));
                }
            }

            return token;
        }

        /// <summary>
        /// Gets the token.
        /// </summary>
        /// <param name="separator">The separator.</param>
        /// <param name="nextLineIfEmpty">if set to <c>true</c> [next line if empty].</param>
        /// <returns>Returns the token</returns>
        public string GetToken(char separator, bool nextLineIfEmpty)
        {
            return this.GetToken(separator, nextLineIfEmpty, false);
        }

        /// <summary>
        /// Gets the token.
        /// </summary>
        /// <param name="separator">The separator.</param>
        /// <returns>Returns the token</returns>
        public string GetToken(char separator)
        {
            return this.GetToken(separator, false, false);
        }

        /// <summary>
        /// Gets the token.
        /// </summary>
        /// <returns>Returns the token.</returns>
        public string GetToken()
        {
            return this.GetToken(' ', false, false);
        }

        /// <summary>
        /// Puts the token.
        /// </summary>
        /// <param name="tok">The token to place.</param>
        public void PutToken(string tok)
        {
            this.lastToken = tok;
        }

        /// <summary>
        /// Gets the line.
        /// </summary>
        /// <returns>Returns the line.</returns>
        public string GetLine()
        {
            return this.line;
        }

        /// <summary>
        /// Determines whether this instance is empty.
        /// </summary>
        /// <returns>
        /// <c>true</c> if this instance is empty; otherwise, <c>false</c>.
        /// </returns>
        public bool IsEmpty()
        {
            return this.line.Equals(string.Empty);
        }
    }
}
